package com.star.security.authentication.wxapp;

import com.star.common.utils.Json;
import com.star.security.authentication.wxapp.response.WxappLoginResponse;
import com.star.security.authentication.wxapp.response.WxappResponse;
import com.star.security.exception.BadCredentialsException;
import com.fasterxml.jackson.annotation.JsonAlias;
import lombok.Getter;
import lombok.Setter;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.web.client.RestTemplate;

import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * @author yangzj
 */
public class WxappAuthenticationApiImpl implements WxappAuthenticationApi {

    private final Wxapp wxapp;
    private final RedissonClient redisson;
    private final RestTemplate restTemplate;

    public WxappAuthenticationApiImpl(Wxapp wxapp, RedissonClient redisson, RestTemplate restTemplate) {
        this.wxapp = wxapp;
        this.redisson = redisson;
        this.restTemplate = restTemplate;
    }

    /**
     * wxapp-api访问令牌 redis key
     */
    private final static String ACCESS_TOKEN_KEY = "wx_app:api_access_token";

    @Override
    public WxappLoginResponse authenticate(String code, Integer type) {
        Wxapp.App app = this.wxapp.getApps().stream().filter(c -> c.getAppType().equals(type)).findFirst()
                .orElseThrow(() -> new BadCredentialsException("微信小程序类型不存在"));
        WxappLoginResponse res = Optional.ofNullable(restTemplate.getForObject(Uri.JS_CODE2_SESSION, String.class
                        , app.getAppId(), app.getAppSecret(), code))
                .map(c -> Json.toBean(c, WxappLoginResponse.class)).orElse(null);
        if (this.fail(res)) {
            throw new BadCredentialsException("微信小程序登录失败" + (res == null ? "" : ": " + res.getErrCode() + ", " + res.getErrMsg()));
        }
        return res;
    }

    /**
     * 获取访问令牌
     *
     * @return {@link String}
     */
    @Override
    public String getAccessToken(Integer type) {
        RBucket<String> bucket = redisson.getBucket(ACCESS_TOKEN_KEY + ":" + type);
        if (bucket.isExists()) {
            return bucket.get();
        }
        Wxapp.App app = this.wxapp.getApps().stream().filter(c -> c.getAppType().equals(type)).findFirst()
                .orElseThrow(() -> new BadCredentialsException("微信小程序类型不存在"));
        AccessTokenResponse res = restTemplate.getForObject(Uri.GET_ACCESS_TOKEN, AccessTokenResponse.class
                , app.getAppId(), app.getAppSecret());
        if (this.fail(res)) {
            throw new BadCredentialsException("微信小程序获取access_token失败" + (res == null ? "" : ": " + res.getErrCode() + ", " + res.getErrMsg()));
        }
        String accessToken = res.getAccessToken();
        bucket.set(accessToken, res.getExpiresIn(), TimeUnit.SECONDS);
        return accessToken;

    }

    /**
     * 失败
     *
     * @param res res
     * @return boolean
     */
    private boolean fail(WxappResponse res) {
        return res == null || res.getErrCode() != null;
    }


    @Getter
    @Setter
    static class AccessTokenResponse extends WxappResponse {
        @JsonAlias("access_token")
        private String accessToken;

        @JsonAlias("expires_in")
        private Long expiresIn;
    }

    interface Uri {

        /**
         * 获取 access_token GET
         */
        String GET_ACCESS_TOKEN = "https://api.weixin.qq.com/cgi-bin/token?appid={appid}&secret={secret}&grant_type=client_credential";

        /**
         * js code2会话
         */
        String JS_CODE2_SESSION = "https://api.weixin.qq.com/sns/jscode2session?appid={appid}&secret={secret}&js_code={jsCode}&grant_type=authorization_code";
    }
}
